package edu.kit.csl.pisa.training.eva2;

/*
This file is part of the PISA Alignment Tool.

Copyright (C) 2013
Karlsruhe Institute of Technology
Cognitive Systems Lab (CSL)
Felix Stahlberg

PISA is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PISA is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PISA. If not, see <http://www.gnu.org/licenses/>.
*/

import java.io.Serializable;

import edu.kit.csl.pisa.datatypes.Alignment;
import edu.kit.csl.pisa.datatypes.SentenceAlignment;
import eva2.server.go.individuals.AbstractEAIndividual;
import eva2.server.go.operators.crossover.InterfaceCrossover;
import eva2.server.go.populations.Population;
import eva2.server.go.problems.InterfaceOptimizationProblem;
import eva2.tools.math.RNG;

/**
 * This cross over operator is specially designed for alignments. It combines
 * the first half of one individual with the second half of another individual.
 * The exact procedure is as follows. First, choose a crossover point p. p is
 * a phoneme position in the target sentence. Second, exchange the alignment
 * after p circular among the parents. Leave the first part of the alignments
 * as they are. This operator is only implemented for sentence alignment 
 * individuals.
 * 
 * @see edu.kit.csl.pisa.datatypes.Alignment
 * 			#combine(edu.kit.csl.pisa.datatypes.Alignment)
 * @see IndividualSentenceAlignment
 */
public class CrossoverLeftRight implements InterfaceCrossover, Serializable {
	
	private static final long serialVersionUID = -6938676747423527345L;
	/*
	 * The next variable is just copied from EvA2 examples.. not sure if we
	 * really need it here.
	 */
	private InterfaceOptimizationProblem    m_OptimizationProblem;
	
	/** 
	 * This method will enable you to clone a given mutation operator. The
	 * only thing to copy is the optimization problem.
	 * 
	 * @return The clone
	 */
	public Object clone() {
		CrossoverLeftRight clone = new CrossoverLeftRight();
		clone.m_OptimizationProblem      = m_OptimizationProblem;
		return clone;
	}
	
	/**
	 * Mate a individual with partners. If one of the individuals is
	 * not a {@link @see IndividualSentenceAlignment} individual, do nothing.
	 * 
	 * @param indy1 the first partner
	 * @param partners set of second partners to choose
	 * @return the next generation
	 * @see IndividualSentenceAlignment
	 */
	@Override
	public AbstractEAIndividual[] mate(AbstractEAIndividual indy1,
			Population partners) {
		final int resLastIndex = partners.size();
		AbstractEAIndividual[]  result = 
				new AbstractEAIndividual[resLastIndex + 1];
        result[0]   = (AbstractEAIndividual) (indy1).clone();
        for (int i = 0; i < partners.size(); i++) {
			result[i + 1] = (AbstractEAIndividual)
					((AbstractEAIndividual) partners.get(i)).clone();
        }
		if (!(indy1 instanceof IndividualSentenceAlignment)
				|| partners.size() == 0) {
	        return result;
		}
		SentenceAlignment[] aligns = new SentenceAlignment[resLastIndex + 1];
		aligns[0] = (SentenceAlignment) ((IndividualSentenceAlignment) indy1)
				.getSentenceAlignment().clone();
		((IndividualSentenceAlignment) result[0]).setGenotype(aligns[0]);
        for (int i = 1; i <= resLastIndex; i++) {
        	aligns[i] = (SentenceAlignment) 
        			((IndividualSentenceAlignment) partners.get(i - 1))
    					.getSentenceAlignment().clone();
        	((IndividualSentenceAlignment) result[i]).setGenotype(aligns[i]);
        }
        float coPoint = RNG.randomInt(5, 95) / 100f;
        if (RNG.randomBoolean()) {
        	Alignment tmp = (Alignment) aligns[0].getAlignment().clone();
        	for (int i = 0; i < resLastIndex; i++) {
            	aligns[i].getAlignment().combine(aligns[i + 1].getAlignment(),
            			coPoint);
            }
        	aligns[resLastIndex].getAlignment().combine(tmp, coPoint);
        } else {
        	Alignment tmp = (Alignment)
        			aligns[resLastIndex].getAlignment().clone();
        	for (int i = resLastIndex; i > 0; i--) {
            	aligns[i].getAlignment().combine(aligns[i - 1].getAlignment(),
            			coPoint);
            	assert aligns[i].isConsistent(); 
            }
        	aligns[0].getAlignment().combine(tmp, coPoint);
        	assert aligns[0].isConsistent();
        }
        // in case the crossover was successful lets give the mutation 
        // operators a chance to mate the strategy parameters
        for (AbstractEAIndividual indy : result) {
        	indy.getMutationOperator().crossoverOnStrategyParameters(indy1,
        			partners);
        }
        return result;
	}

	/** 
     * Remember the optimization problem.
     * {@inheritDoc}
     */
	@Override
	public void init(AbstractEAIndividual individual,
			InterfaceOptimizationProblem opt) {
		this.m_OptimizationProblem = opt;
	}

	/* (non-Javadoc)
	 * @see eva2.server.go.operators.crossover.InterfaceCrossover
	 * 		#getStringRepresentation()
	 */
	@Override
	public String getStringRepresentation() {
		return "Left-right alignment crossover operator";
	}

}
